home *** CD-ROM | disk | FTP | other *** search
- Path: solon.com!not-for-mail
- From: msb@sq.com
- Newsgroups: comp.std.c,comp.lang.c.moderated
- Subject: Re: Integral promotion.
- Date: 15 Feb 1996 08:44:06 -0600
- Organization: SoftQuad Inc., Toronto, Canada
- Sender: clc@solutions.solon.com
- Approved: clc@solutions.solon.com
- Message-ID: <4fvgrm$dv9@solutions.solon.com>
- References: <4fstj7$2l6@solutions.solon.com> <4fu835$9de@solutions.solon.com>
- NNTP-Posting-Host: solutions.solon.com
-
- Oh dear, I have to correct Chris Torek (torek@bsdi.com). Most of his
- article was correct, of course; please review it if you need more context.
-
- The question was about
-
- > > short test(short x1, short x2)
- > > {
- > > short result;
- > > result = x1 + x2; /* Warning: '=' : conversion from 'int '
- > > to 'short ', possible loss of data */
- > > return result;
- > > }
-
- Chris writes:
-
- > For efficiency and/or less-surprising behavior, ANSI C says that
- > prototyped parameters (as you have here) are not promoted. (Many
- > compilers simply promote them at the caller, and then narrow them
- > before entering the function, just as for Old C, but they can now
- > avoid these steps in this case.)
-
- It is correct that prototyped arguments do not undergo the default
- *argument* promotions, but they still undergo the *integral* promotions.
- Thus even in ANSI C, if this function is called this way:
-
- extern short p, q, r, test(short,short);
- r = test(p,q);
-
- the values from p and q are still converted from short to int and back
- to short. However, there is a difference from old C. In old C, what
- happened was that p and q were converted to int in the caller, two ints
- were given to test(), and these int values were *then* converted back to
- shorts to assign them to x1 and x2. In ANSI C, with the prototype in
- scope, p and q are converted to int and then *immediately* back to
- short; two shorts are passed to test(), and these values are assigned
- to x1 and x2.
-
- And this difference is significant, because the caller and test() may
- be compiled separately. This means that in old C, the compiler would
- *have* to perform the conversions; but in ANSI C, because the conversion
- is reversed within the same expression, it is trivial for the compiler
- to optimize it out.
-
- In the code originally posted, the call was test(1,2). Since the
- arguments in this case are ints, a conversion is always required.
-
-
- This is different from the case of an argument of type float. In old C,
- just as shorts were automatically promoted to int anywhere, so floats
- were to double. In ANSI C, *this* automatic promotion has been completely
- removed -- except when it forms part of the default argument promotions.
-
- This paragraph of Chris's is worth reemphasizing:
-
- > Because ANSI C requires promotion for non-prototypes and non-promotion
- > for prototypes, it is important to make sure you never mix up the
- > two kinds of declarations and definitions when you use `narrow'
- > types (char, short, signed char, unsigned char, unsigned short,
- > and float).
-
-
- Now to the inside of the function.
-
- > result = x1 + x2;
-
- > ... if `int' and `short' are the same width, a result that would
- > be outside the range [SHRT_MIN .. SHRT_MAX] is also outside the
- > range [INT_MIN .. INT_MAX], and the behavior is undefined (ANSI C
- > section 3.3, p. 39, ll. 15--17).
-
- Right. If int and short are the same width, overflow can occur, with
- underfined behavior.
-
- > On the other hand, if `int's are wider than `short's ... the sum is
- > not outside the representable range ... The final step is to convert
- > the sum back to type `short'. Here things get a little fuzzy....
-
- No, they don't.
-
- > comp.std.c has had arguments as to what happens if the sum is `int',
- > `int' is wider than `short', and the sum is not representable as a `short'.
-
- Only arguments between -- to be blunt -- people who know what they are
- talking about and those who do not. Needless to say, Chris is usually
- in the former category, but not this time. The result of the conversion
- in this case is implementation-defined, and undefined behavior does not
- occur. Chapter and verse below.
-
- > The Standard distinguishes between `implicit' and `explicit'
- > conversions ...
-
- It makes no distinction whatever between their semantics, except that in
- certain cases "that involve pointers" (quote from 6.3.4/3.3.4), it requires
- the conversions to be explicit. We're not talking about pointers here.
-
- > Some have argued that an explicit conversion should never cause an
- > overflow, but there seems to be no wording in the standard to justify
- > this position. There appears to be agreement that implicit conversion
- > might overflow. The behavior on overflow would be undefined.
-
- Completely wrong, assuming that "overflow" is meant to imply an error
- condition. It might be sensible, but it's not what the standard specifies.
- Chapter and verse below.
-
- > It is interesting, however, to note that if overflow will ever occur,
- > it will occur no matter whether the ranges of short and int coincide
- > or not. The only thing that changes here is the exact operation that
- > overflows: the sum, or the implicit conversion in the assignment.
-
- This would be correct if "overflow" is *not* taken as implying an error
- condition, but merely the occurrence of a value that will not fit.
-
-
- Excerpts from the standard:
-
- * From 6.3.16.1/3.3.16.1:
-
- # In "simple assignment" (=), the value of the right operand is
- # converted to the type of the left operand ...
-
- * From 6.2.1.2/3.2.1.2:
-
- # When a value with integral type is demoted to a signed integer with
- # smaller size ... if the value cannot be represented the result is
- # implementation-defined.
-
- Hence implementation-defined behavior occurs.
-
- * From 3.10/1.6 (emphasis added):
-
- # Implementation-defined behavior -- behavior, for a CORRECT program
- # construct and CORRECT data, that depends on the characteristics of
- # the implementation and that each implementation shall document.
-
- * From 3.16/1.6 (emphasis added):
-
- # Undefined behavior -- behavior, upon use of a NONPORTABLE OR ERRONEOUS
- # program construct, of ERRONEOUS data, or of indeterminately-valued
- # objects, for which the Standard imposes no requirements. ...
-
- Hence implementation-defined and undefined behavior are mutually
- exclusive; the occurrence of implementation-defined behavior implies
- that the construct is correct; and so, in the case in question where
- int is wider than short, undefined behavior cannot occur.
- --
- Mark Brader "Nicely self-consistent. (Pay no attention to
- msb@sq.com that D-floating number behind the curtain!)"
- SoftQuad Inc., Toronto -- Chris Torek, on pasta
-
- My text in this article is in the public domain.
-